package com.wx.tedu.file;


import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 处理关于Excel的文件
 *
 * @author 29623
 */
public class FileMethodExcel {

    /**
     * 读取两张Excel取交集(包含主键：工号 相同则生成整行)
     *
     * @param firstExcelName  Excel表1路径
     * @param secondExcelName Excel表2路径
     * @param outputExcelName Excel表3输出表路径
     */
    public void readPrimaryKeyExcel(String firstExcelName, String secondExcelName, String outputExcelName) {


        writeExcelOfIdAll( compareTwoExcelOfIdGetValueAll(readExcel(firstExcelName), readExcel(secondExcelName)),outputExcelName);
        System.out.println(" is successfully read and write！！！");
    }

    /**
     * 比较工号相同则保存整行数据
     * 判断两个Excel表交集----key和List<String>value--读Excel时已删除key所在的元素下标
     *
     * @param map1 传入读取到的第一个Excel表
     * @param map2 传入读取到的第二个Excel表
     * @return LinkedHashMap<String, List<String>>
     */
    private LinkedHashMap<String, List<String>> compareTwoExcelOfIdGetValueAll (LinkedHashMap<String, List<String>> map1,
                                                                                LinkedHashMap<String, List<String>> map2){

        LinkedHashMap<String, List<String>> result = new LinkedHashMap<>();

        for (Map.Entry<String, List<String>> entry1 : map1.entrySet()) {

            for (Map.Entry<String, List<String>> entry2 : map2.entrySet()) {

                if (entry2.getKey().equals(entry1.getKey())){
                    result.put(entry2.getKey(),entry1.getValue());
                }

            }
        }

        return result;
    }

    /**
     * 比较工号不相同则保存整行数据
     * 判断两个Excel表交集----key和List<String>value--读Excel时已删除key所在的元素下标
     *
     * @param map1 传入读取到的第一个Excel表
     * @param map2 传入读取到的第二个Excel表
     * @return LinkedHashMap<String, List<String>>
     */
    private LinkedHashMap<String, List<String>> compareTwoExcelOfDifferentIdGetValueAll (LinkedHashMap<String, List<String>> map1,
                                                                       LinkedHashMap<String, List<String>> map2){

        LinkedHashMap<String, List<String>> result = new LinkedHashMap<>();

        for (Map.Entry<String, List<String>> entry1 : map1.entrySet()) {

            for (Map.Entry<String, List<String>> entry2 : map2.entrySet()) {

                if (entry2.getKey().equals(entry1.getKey())){
                    result.put(entry2.getKey(),entry2.getValue());
                }

            }
        }

        return result;
    }

    /**
     * 比较工行相同则保存工号和姓名
     * 判断两个Excel表交集----cell为0和1----key和value.get(0)--读Excel时已删除key所在的元素下标
     *
     * @param map1 传入读取到的第一个Excel表
     * @param map2 传入读取到的第二个Excel表
     * @return LinkedHashMap<String, String>
     */
    private LinkedHashMap<String, String> compareTwoExcelOfIdGetValue (LinkedHashMap<String, List<String>> map1,
                                                                     LinkedHashMap<String, List<String>> map2){

        LinkedHashMap<String, String> result = new LinkedHashMap<>();

        for (Map.Entry<String, List<String>> entry1 : map1.entrySet()) {
            String key1 = entry1.getKey();
            List<String> value1 = entry1.getValue();

            for (Map.Entry<String, List<String>> entry2 : map2.entrySet()) {
                String key2 = entry2.getKey();
                List<String> value2 = entry2.getValue();

                if (key2.equals(key1)&&value2.get(0).equals(value1.get(0))){
                    result.put(key2,value2.get(0));
                }

            }
        }

        return result;
    }

    /**
     * 比较工行相同则保存工号和姓名
     * 生成Excel为key，value两列
     * 根据读取返回为LinkedHashMap<String, String>map类型生成Excel
     *
     * @param map 传入要生成的key--工号，value--String
     * @param excelName 生成Excel的地址/名称
     */

    private void writeExcelOfId( LinkedHashMap<String, String > map, String excelName) {

        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("result");

        int rowNum = 0;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            Row row = sheet.createRow(rowNum++);
            String key = entry.getKey();
            String value = entry.getValue();

            Cell cell0 = row.createCell(0);
            cell0.setCellValue(key);
            Cell cell1 = row.createCell(1);
            cell1.setCellValue(value);
        }

        try {
            FileOutputStream outputStream = new FileOutputStream(excelName);
            workbook.write(outputStream);
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 工号相同则保存整行数据
     * 生成Excel key，values整行
     * 根据读取返回为LinkedHashMap<String, List<String>>map类型生成Excel
     *
     * @param map 传入要生成的key--工号，value--List<String>
     * @param excelName 生成Excel的地址/名称
     */

    private void writeExcelOfIdAll( LinkedHashMap<String, List<String>> map, String excelName) {

        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("result");

        int rowNum = 0;
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            Row row = sheet.createRow(rowNum++);
            String key = entry.getKey();
            List<String> value = entry.getValue();

            Cell cell0 = row.createCell(0);
            cell0.setCellValue(key);

            int cellNum = 1;
            for (String str : value) {
                Cell cell = row.createCell(cellNum++);
                cell.setCellValue(str);
            }
        }

        try {
            FileOutputStream outputStream = new FileOutputStream(excelName);
            workbook.write(outputStream);
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据工号为唯一标识读取Excel生成整行
     *
     * @param excelName 传入文件地址
     * @return LinkedHashMap<String , List <String> >map key--工号，value--<List String >
     */
    private LinkedHashMap<String, List<String>> readExcel(String excelName) {

        LinkedHashMap<String, List<String>> map = new LinkedHashMap<>();

        try {
            FileInputStream file1 = new FileInputStream(excelName);
            Workbook workbook = WorkbookFactory.create(file1);
            Sheet sheet = workbook.getSheetAt(0);

            for (Row row : sheet) {
                List<String> orderedSet = new ArrayList<>();
                for (Cell cell : row) {
                    orderedSet.add(chooseType(cell));
                }
                orderedSet.remove(0);
                map.put(chooseType(row.getCell(0)), orderedSet);
            }
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return map;
    }

    /**
     * 将各个单元格转成string格式进行操作
     *
     * @param cell 传入单元格进行判断
     * @return 处理各个单元格为string格式
     */
    private String chooseType(Cell cell) {

        String styleDate = "yyyy-MM-dd";// 自定义日期格式

        //判断是否是数值类型
        if (cell.getCellType() == CellType.NUMERIC) {
            //判断是否是日期类型
            if (DateUtil.isCellDateFormatted(cell)) {
                // 处理日期类型的单元格
                return convertDateFormat(cell, styleDate);
                //判断是否是整数类型（1，2，3、、、、）
            } else if (isIntegerBigDecimal(cell.getNumericCellValue())) {
                // 处理整数类型的单元格，四舍五入(保险机制)
                return convertRoundingModeBigDecimal(cell.getNumericCellValue());
                //判断是否是整数类型（1，2，3、、、、）
            } else if (!isIntegerBigDecimal(cell.getNumericCellValue())) {
                // 处理浮点数类型的单元格
                return String.valueOf(cell.getNumericCellValue());
                /*处理货币类型的单元格
                $美元、加拿大元、澳大利亚元等
                €欧元
                £英镑
                ¥日元
                ￥人民币 */
                /* cell.getCellStyle().getDataFormatString().contains("$") ||
                    cell.getCellStyle().getDataFormatString().contains("€") ||
                    cell.getCellStyle().getDataFormatString().contains("£") ||
                    cell.getCellStyle().getDataFormatString().contains("¥") ||
                    cell.getCellStyle().getDataFormatString().contains("￥")||*/
                //使用正则表达式优化代码逻辑
            } else if (
            Pattern.compile("[$€£¥￥]")
                    .matcher(cell.getCellStyle().getDataFormatString()).find()) {
                // 处理货币类型的单元格
                return convertCurrencyFormat(cell);
            }
            //判断是否是字符串类型
        } else if (cell.getCellType() == CellType.STRING) {
            // 处理字符串类型的单元格
            return cell.getStringCellValue();
            //判断是否是布尔类型
        } else if (cell.getCellType() == CellType.BOOLEAN) {
            // 处理布尔类型的单元格
            return String.valueOf(cell.getBooleanCellValue());
            //判断是否是公式类型
        } else if (cell.getCellType() == CellType.FORMULA) {
            // 处理公式类型的单元格
            return convertFormula(cell);
            //判断是否是空单元格
        } else if (cell.getCellType() == CellType.BLANK) {
            //处理空表格
            /*
            实际上会返回字符串"BLANK"，而不是空白单元格的值。
            return String.valueOf(CellType.BLANK);
             */
            return " ";
            //判断是否是错误的单元格
        } else if (cell.getCellType() == CellType.ERROR) {
            //处理错误的单元格
            return "ERROR";
        }
        return "此单元格未处理 ";
    }

    /**
     * 将传入的日期单元格按照自定义的日期格式转换为string
     *
     * @param cell      传入单元格的值
     * @param styleDate 指定日期样式
     * @return String
     */
    private String convertDateFormat(Cell cell, String styleDate) {
        DateFormat dateFormat = new SimpleDateFormat(styleDate); // 自定义日期格式
        return dateFormat.format(cell.getDateCellValue());
    }

    /**
     * 将传入的货币单元格转换为String
     *
     * @param cell 传入单元格的值
     * @return String
     */
    private String convertCurrencyFormat(Cell cell) {
        NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(); // 货币格式
        return currencyFormat.format(cell.getNumericCellValue());
    }

    /**
     * 将数值单元格四舍五入为整数，转换为String
     *
     * @param doubleNumber 根据传入单元格进行四舍五入为整数
     * @return String
     */
    private String convertRoundingModeBigDecimal(Double doubleNumber) {
        /*
        使用setScale(0, RoundingMode.HALF_UP)方法对BigDecimal对象进行四舍五入，
        然后调用intValue()方法获取整数值。
         */
        return BigDecimal.valueOf(doubleNumber).setScale(0, RoundingMode.HALF_UP).toPlainString();
    }

    /**
     * 将公式单元格进行计算后，转换为String
     *
     * @param cell 传入单元的值
     * @return String
     */
    private String convertFormula(Cell cell) {
            /*
            首先，使用 cell.getSheet() 方法获取到包含该单元格的 Sheet 对象。
            通过 sheet.getWorkbook() 方法获取到包含该工作表的 Workbook 对象。
            通过Workbook对象的getCreationHelper()方法创建一个CreationHelper对象。
            CreationHelper对象是用于处理Excel数据类型转换和公式求值等操作的工具类。
            createFormulaEvaluator()方法是CreationHelper接口中的一个方法，
            用于创建一个FormulaEvaluator对象，用于计算Excel单元格中的公式。
            FormulaEvaluator对象可以对Excel单元格中的公式进行求值，并返回计算结果。
            通过evaluator.evaluate(cell)方法对指定的单元格进行求值操作，
            并将计算结果存储在CellValue对象中。
            evaluate()方法会根据单元格中的公式类型和内容进行相应的计算，如果单元格不包含公式，则直接返回单元格的值。
            */
        FormulaEvaluator evaluator = cell.getSheet().getWorkbook().getCreationHelper().createFormulaEvaluator();
        CellValue cellValue = evaluator.evaluate(cell);
        return cellValue.formatAsString(); // 将公式计算结果转换为字符串
    }

    /**
     * 判断数值单元格是否为整数类型
     *
     * @param doubleNumber 根据传入单元格判断数值小数位数是否为整数
     * @return boolean
     */
    private boolean isIntegerBigDecimal(Double doubleNumber) {
        /*
        该方法将Double类型的数字转换成BigDecimal对象，
        并使用stripTrailingZeros()方法去除小数点后的所有零。
        使用scale()方法获取BigDecimal对象的小数位数，
        如果小数位数为0，则说明该数字为整数，返回true；否则，返回false。
         */
        return BigDecimal.valueOf(doubleNumber).stripTrailingZeros().scale() <= 0;
    }

    /**
     * 判断数值单元格是否为浮点类型
     *
     * @param doubleNumber 根据传入单元格判断数值小数位数是否为整数
     * @return boolean
     */
    private boolean isDoubleBigDecimal(Double doubleNumber) {
        /*
        该方法将Double类型的数字转换成BigDecimal对象，
        并使用stripTrailingZeros()方法去除小数点后的所有零。
        使用scale()方法获取BigDecimal对象的小数位数，
        如果小数位数不为0，则说明该数字为浮点数，返回true；否则，返回false。
         */
        return BigDecimal.valueOf(doubleNumber).stripTrailingZeros().scale() >= 0;
    }


}
